﻿using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json; 
using SSPivot.Common;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;

namespace StackExchange.Redis
{
    public static class RedisExtensions
    {
        #region 同步
        #region 普通操作
        /// <summary>
        /// 是否存在key
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool HasKey(this IDatabase cache, string key)
        {
            return cache.KeyExists(key);
        }
        #region string
        public static int RemoveKeys(this IDatabase cache, string[] keys)
        {
            return cache.Remove(keys);
        }

        /// <summary>
        /// String取值(对象)(同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T Get<T>(this IDatabase cache, string key)
        {
            return cache.StringGet(key).ToString().DeserializeJson<T>();
        }

        public static string Get(this IDatabase cache, string key)
        {
            return cache.StringGet(key).ToString();
        }

        public static int Remove(this IDatabase cache, params string[] keys)
        {
            return cache.Remove(keys);
        }

        public static bool Set<T>(this IDatabase cache, string key, T value, TimeSpan? timeSpan = null)
        {
            return cache.StringSet(key, value.SerializeJson(), timeSpan);
        }


        /// <summary>
        /// 获取或添加缓存（String类型）(异步)
        /// </summary>
        /// <typeparam name="TCacheItem"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <param name="minutes"></param>
        /// <returns></returns>

        public static TCacheItem GetOrAdd<TCacheItem>(this IDatabase cache, string key, Func<TCacheItem> factory, TimeSpan? timeSpan = null)
        {
            TCacheItem cacheItem = cache.Get<TCacheItem>(key);
            if (cacheItem == null)
            {
                cacheItem = factory.Invoke();
                if (cacheItem != null)
                {
                    if (timeSpan == null)
                        cache.Set(key, cacheItem);
                    else
                        cache.Set(key, cacheItem, timeSpan.Value);

                }
            }
            return cacheItem;
        }
        public static bool SetExpire(this IDatabase cache, string key, TimeSpan timeSpan)
        {
            return cache.KeyExpire(key, timeSpan);
        }

        public static DateTime? GetExpire(this IDatabase cache, string key)
        {
            return cache.KeyExpireTime(key);
        }
        #endregion

        #region hash

        public static bool HashAdd<T>(this IDatabase cache, string key, string hashKey, T value)
        {
            return cache.HashSet(key, hashKey, value.SerializeJson());
        }
        public static bool HashAddString(this IDatabase cache, string key, string hashKey, string value)
        {
            return cache.HashSet(key, hashKey, value);
        }
        public static void HashSet<T>(this IDatabase db, string key, Dictionary<string, T> hash)
        {
            var entries = hash.Select(kv => new HashEntry(kv.Key, JsonConvert.SerializeObject(kv.Value))).ToArray();
            db.HashSet(key, entries);
        }

        public static long HashDel(this IDatabase cache, string key, RedisValue[] fields)
        {
            return cache.HashDelete(key, fields);
        }

        /// <inheritdoc/>
        public static T HashGetOne<T>(this IDatabase cache, string key, string field)
        {
            return cache.HashGet(key, field).ToString().DeserializeJson<T>();
        }
        public static string HashGetString<T>(this IDatabase cache, string key, string field)
        {
            return cache.HashGet(key, field).ToString();
        }

        public static Dictionary<string, T> HashGetAll<T>(this IDatabase db, string key)
        {
            var entries = db.HashGetAll(key);
            return entries.ToDictionary(
                x => x.Name.ToString(),
                x => JsonConvert.DeserializeObject<T>(x.Value));
        }


        /// <summary>
        /// 获取或添加缓存(HASH类型)(同步)
        /// </summary>
        /// <typeparam name="TCacheItem"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <param name="minutes"></param>
        /// <returns></returns>
        public static TCacheItem HashGetOrAdd<TCacheItem>(this IDatabase cache, string key, string field, Func<TCacheItem> factory)
        {
            TCacheItem cacheItem = cache.HashGetOne<TCacheItem>(key, field);
            if (cacheItem == null)
            {
                cacheItem = factory.Invoke();
                if (cacheItem != null)
                    cache.HashAdd(key, field, cacheItem);
            }
            return cacheItem;
        }
        #endregion

        #endregion 普通操作

        
         
        #region lua
        /// <summary>
        /// 执行脚本
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="keys"> 用于定位分区节点，不含prefix前辍</param>
        /// <param name="script"> </param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static RedisResult ExecuteLuaScript(this IDatabase cache, string script, RedisKey[] keys, params object[] args)
        {
            return cache.ScriptEvaluate(script, keys, args.Select(v => (RedisValue)v).ToArray());
        }
        #endregion
        #endregion

        #region 异步
        #region 普通操作
        /// <summary>
        /// 是否存在key
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<bool> HasKeyAsync(this IDatabase cache, string key)
        {
            return await cache.KeyExistsAsync(key);
        }
        #region string
        public static async Task<int> RemoveKeysAsync(this IDatabase cache, string[] keys)
        {
            return await cache.RemoveKeysAsync(keys);
        }

        /// <summary>
        /// String取值(对象)(同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T> GetAsync<T>(this IDatabase cache, string key)
        {
            return JsonConvert.DeserializeObject<T>(await cache.StringGetAsync(key));
        }

        public static async Task<string> GetStringAsync(this IDatabase cache, string key)
        {
            return await cache.StringGetAsync(key);
        }
         

        public static async Task<bool> SetAsync<T>(this IDatabase cache, string key, T value, TimeSpan? timeSpan = null)
        {
            return await cache.StringSetAsync(key, value.SerializeJson(), timeSpan);
        }


        /// <summary>
        /// 获取或添加缓存（String类型）(异步)
        /// </summary>
        /// <typeparam name="TCacheItem"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <param name="minutes"></param>
        /// <returns></returns>

        public static async Task<TCacheItem> GetOrAddAsync<TCacheItem>(this IDatabase cache, string key, Func<TCacheItem> factory, TimeSpan? timeSpan = null)
        {
            TCacheItem cacheItem = await cache.GetAsync<TCacheItem>(key);
            if (cacheItem == null)
            {
                cacheItem = factory.Invoke();
                if (cacheItem != null)
                {
                    if (timeSpan == null)
                        await cache.SetAsync(key, cacheItem);
                    else
                        await cache.SetAsync(key, cacheItem, timeSpan.Value);

                }
            }
            return cacheItem;
        }
        public static async Task<bool> SetExpireAsync(this IDatabase cache, string key, TimeSpan timeSpan)
        {
            return await cache.KeyExpireAsync(key, timeSpan);
        }

        public static async Task<DateTime?> GetExpireAsync(this IDatabase cache, string key)
        {
            return await cache.KeyExpireTimeAsync(key);
        }
        #endregion

        #region hash

        public static async Task<bool> HashAddAsync<T>(this IDatabase cache, string key, string hashKey, T value)
        {
            return await cache.HashSetAsync(key, hashKey, value.SerializeJson());
        }
        public static async Task<bool> HashAddStringAsync(this IDatabase cache, string key, string hashKey, string value)
        {
            return await cache.HashSetAsync(key, hashKey, value);
        }
        public static async Task HashSetAsync<T>(this IDatabase db, string key, Dictionary<string, T> hash)
        {
            var entries = hash.Select(kv => new HashEntry(kv.Key, JsonConvert.SerializeObject(kv.Value))).ToArray();
            await db.HashSetAsync(key, entries);
        }

        public static async Task<long> HashDelAsync(this IDatabase cache, string key, RedisValue[] fields)
        {
            return await cache.HashDeleteAsync(key, fields);
        }

        /// <inheritdoc/>
        public static async Task<T> HashGetOneAsync<T>(this IDatabase cache, string key, string field)
        {
            return JsonConvert.DeserializeObject<T>(await cache.HashGetAsync(key, field));
        }
        public static async Task<string> HashGetStringAsync<T>(this IDatabase cache, string key, string field)
        {
            return await cache.HashGetAsync(key, field);
        }

        public static async Task<Dictionary<string, T>> HashGetAllAsync<T>(this IDatabase db, string key)
        {
            var entries = await db.HashGetAllAsync(key);
            return entries.ToDictionary(
                x => x.Name.ToString(),
                x => JsonConvert.DeserializeObject<T>(x.Value));
        }


        /// <summary>
        /// 获取或添加缓存(HASH类型)(同步)
        /// </summary>
        /// <typeparam name="TCacheItem"></typeparam>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <param name="minutes"></param>
        /// <returns></returns>
        public static async Task<TCacheItem> HashGetOrAddAsync<TCacheItem>(this IDatabase cache, string key, string field, Func<TCacheItem> factory)
        {
            TCacheItem cacheItem = await cache.HashGetOneAsync<TCacheItem>(key, field);
            if (cacheItem == null)
            {
                cacheItem = factory.Invoke();
                if (cacheItem != null)
                    await  cache.HashAddAsync(key, field, cacheItem);
            }
            return cacheItem;
        }
        #endregion

        #endregion 普通操作



        #region lua
        /// <summary>
        /// 执行脚本
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="keys"> 用于定位分区节点，不含prefix前辍</param>
        /// <param name="script"> </param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static async Task<RedisResult> ExecuteLuaScriptAsync(this IDatabase cache, string script, RedisKey[] keys, params object[] args)
        {
            return await cache.ScriptEvaluateAsync(script, keys, args.Select(v => (RedisValue)v).ToArray());
        }
        #endregion
        #endregion

    }

}